﻿using System;
using System.Text;

namespace WLFramework.Data.Sql.SqlProvider
{
	public class MsSql2012SqlProvider : MsSqlSqlProvider
	{
		protected override string LimitFormat         { get { return SqlQuery.Select.SkipValue != null ? "FETCH NEXT {0} ROWS ONLY" : null; } }
		protected override string OffsetFormat        { get { return "OFFSET {0} ROWS"; } }
		protected override bool   OffsetFirst         { get { return true;              } }
		protected override bool   BuildAlternativeSql { get { return false;             } }

		protected override ISqlProvider CreateSqlProvider()
		{
			return new MsSql2012SqlProvider();
		}

		protected override void BuildInsertOrUpdateQuery(StringBuilder sb)
		{
			BuildInsertOrUpdateQueryAsMerge(sb, null);
			sb.AppendLine(";");
		}

		protected override void BuildSql(StringBuilder sb)
		{
			if (NeedSkip && SqlQuery.OrderBy.IsEmpty)
			{
				for (var i = 0; i < SqlQuery.Select.Columns.Count; i++)
					SqlQuery.OrderBy.ExprAsc(new SqlValue(i + 1));
			}

			base.BuildSql(sb);
		}

        protected override void BuildFunction(StringBuilder sb, SqlFunction func)
        {
            func = ConvertFunctionParameters(func);

            switch (func.Name)
            {
                case "CASE":
                    func = ConvertCase(func.SystemType, func.Parameters, 0);
                    break;

                case "Coalesce":
                    if (func.Parameters.Length > 2)
                    {
                        var parms = new ISqlExpression[func.Parameters.Length - 1];

                        Array.Copy(func.Parameters, 1, parms, 0, parms.Length);
                        BuildFunction(
                            sb,
                            new SqlFunction(
                                func.SystemType,
                                func.Name,
                                func.Parameters[0],
                                new SqlFunction(func.SystemType, func.Name, parms)));

                        return;
                    }

                    var sc = new SqlQuery.SearchCondition();
                    sc.Conditions.Add(
                        new SqlQuery.Condition(
                            false,
                            new SqlQuery.Predicate.IsNull(func.Parameters[0], false)));
                    func = new SqlFunction(
                        func.SystemType,
                        "IIF",
                        sc,
                        func.Parameters[1],
                        func.Parameters[0]);
                    break;
            }

            base.BuildFunction(sb, func);
        }

        static SqlFunction ConvertCase(Type systemType, ISqlExpression[] parameters, int start)
        {
            var len = parameters.Length - start;
            var name = start == 0 ? "IIF" : "CASE";
            var cond = parameters[start];

            if (start == 0 && SqlExpression.NeedsEqual(cond))
            {
                cond = new SqlQuery.SearchCondition(
                    new SqlQuery.Condition(
                        false,
                        new SqlQuery.Predicate.ExprExpr(
                            cond,
                            SqlQuery.Predicate.Operator.Equal,
                            new SqlValue(1))));
            }

            if (len == 3)
            {
                return new SqlFunction(
                    systemType,
                    name,
                    cond,
                    parameters[start + 1],
                    parameters[start + 2]);
            }

            return new SqlFunction(
                systemType,
                name,
                cond,
                parameters[start + 1],
                ConvertCase(systemType, parameters, start + 2));
        }
	}
}
